/*
 * =====================================================================================
 *
 *       Filename:  cl_buffer.cxx
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  04/05/2011 12:17:53 PM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Chen Ken (Ken), zlchen.ken@gmail.com
 *        Company:  EMC
 *
 * =====================================================================================
 */

#include <cassert>
#include "cl_buffer.hxx" 
#include "cl_socket_prototypes.hxx"

namespace neo
{
using namespace std;

CLBuffer::CLBuffer() 
  : m_buf(BUF_HEAD_RES_SIZE + BUF_INIT_SIZE),
  m_readerIndex(BUF_HEAD_RES_SIZE),
  m_writerIndex(BUF_HEAD_RES_SIZE)
{
}

size_t CLBuffer::readableBytes(void) const 
{
  return m_writerIndex - m_readerIndex;
}

size_t CLBuffer::writableBytes(void) const 
{
  return m_buf.size() - m_writerIndex;
}

size_t CLBuffer::discardableBytes(void) const 
{
  return m_readerIndex;
}

void CLBuffer::write(const std::string& data) 
{
  write(data.data(), data.size());
}

void CLBuffer::write(const void* data, size_t siz) 
{
  assert(data != NULL);
  if(siz > writableBytes())
  {
    grow(siz);
  }
  memmove(writableBegin(), data, siz);
  m_writerIndex += siz;
}

// Read siz bytes to "data" as a string. If siz is greater 
// than readableBytes, return as much as possible.
size_t CLBuffer::read(std::string& data, size_t siz) 
{
  if(siz == 0)
	{
    siz = readableBytes();
	}
	else if(siz > readableBytes())
	{
		siz = readableBytes();
	}
  string tmp(static_cast<const char*>(readableBegin()), siz);
  data.swap(tmp);
  m_readerIndex += siz;
  return siz;
}

size_t CLBuffer::read(void* data, size_t siz) 
{
  assert(data != NULL);
  size_t rsiz = (siz > readableBytes() ? readableBytes(): siz);
  memmove(data, readableBegin(), rsiz);
  m_readerIndex += siz;
  return rsiz;
}

// Read data from fd, and fill the internal buffer.
// If clients specifies siz(!=0) bytes to read, read 
// exactly siz bytes like read(2).
ssize_t CLBuffer::readfd(int fd, size_t siz) 
{
  ssize_t rbytes = 0;
  if(siz > 0)
  {
    // If we reach the end of file, we may allocate
    // the memory unnecessary.
    if(siz > writableBytes())
    {
      grow(siz);
    }
    assert(writableBytes() >= siz);
    rbytes = clRead(fd, writableBegin(), siz);
    if(rbytes > 0)
    {
      m_writerIndex += rbytes;
    }
  }
  else
  {
    size_t wbytes = writableBytes();
    char buf[32*1024];
    struct iovec vbuf[2];
    vbuf[0].iov_base = writableBegin();
    vbuf[0].iov_len = wbytes;
    vbuf[1].iov_base = buf;
    vbuf[1].iov_len = sizeof(buf);
    rbytes = clReadv(fd, vbuf, 2);
    if(rbytes < 0)
    {
    }
    else if(static_cast<size_t>(rbytes) <= wbytes)
    {
      m_writerIndex += rbytes;
    }
    else
    {
      m_writerIndex = m_buf.size();
      write(buf, rbytes - wbytes);
    }
  }

  return rbytes;
}

// Write data to fd, and adjust the internal buffer.
// If clients specifies siz(!=0) bytes to write, write 
// exactly siz bytes like write(2).
ssize_t CLBuffer::writefd(int fd, size_t siz)
{
  size_t rbytes = readableBytes();
  if((siz > 0) && (siz > rbytes))
	{
    siz = rbytes;
	}

  ssize_t wbytes = 0;
  if(rbytes > 0)
	{
    wbytes = clWrite(fd, readableBegin(), siz);
    if(wbytes < 0)
		{
		}
		else
		{
      m_readerIndex += siz;
		}
	}
  return wbytes;
}

ssize_t CLBuffer::writefd(int fd, const void* data, size_t siz)
{
	ssize_t wbytes = 0;
  size_t rbytes = readableBytes();
	if(rbytes > 0)
	{
    struct iovec vbuf[2];  
	  vbuf[0].iov_base = const_cast<void*>(readableBegin());
		vbuf[0].iov_len = rbytes;
		vbuf[1].iov_base = const_cast<void*>(data);
		vbuf[1].iov_len = siz;
    wbytes = clWritev(fd, &vbuf[0], 2);
    if(wbytes < 0)
		{
		}
		else if(static_cast<size_t>(wbytes) < rbytes)
		{
      m_readerIndex += wbytes;
	    write(data, siz);
		}
		else if(static_cast<size_t>(wbytes) < rbytes + siz)
		{
      m_readerIndex += rbytes;
      write((char*)data + (wbytes - rbytes), (rbytes + siz) - wbytes);
		}
		else
		{
	    // Completely writing....
			m_readerIndex += rbytes;
		}
	}
	else
	{
    wbytes = clWrite(fd, data, siz);
    if(wbytes < 0)
		{
		}
		else if(static_cast<size_t>(wbytes) < siz)
		{
			write((char*)data + wbytes, siz - wbytes);
		}
	}
  return wbytes;
}

const void* CLBuffer::readableBegin(void) const
{
  return &m_buf[m_readerIndex];
}

void CLBuffer::reset(void) 
{
  m_readerIndex = BUF_HEAD_RES_SIZE;
  m_writerIndex = BUF_HEAD_RES_SIZE;
}

size_t CLBuffer::size(void) const
{
  return m_buf.size();
}

void* CLBuffer::writableBegin(void) 
{
  return &m_buf[m_writerIndex];
}

inline void CLBuffer::grow(size_t siz)
{
  if((writableBytes() + discardableBytes()) < (siz + BUF_HEAD_RES_SIZE))  
  {
    m_buf.resize(m_writerIndex + siz);
  }
  else
  {
    size_t rbytes = readableBytes();
    memmove(&m_buf[BUF_HEAD_RES_SIZE], readableBegin(), rbytes);
    m_readerIndex = BUF_HEAD_RES_SIZE;
    m_writerIndex = m_readerIndex + rbytes;
  }
}

} // neo

